/*****************************************************************//**
 * \file   StringTool.h
 * \brief  StringTool
 *
 * \author Wyatt
 * \date   May 2023
 *********************************************************************/
#pragma once

#include <vector>
#include <string>
#include <unordered_map>
#include <algorithm>
#include <functional>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>

#include "Define.h"

inline std::string& Trim(std::string& value);
inline std::string& Trim(std::string&& value);
inline std::string TrimView(std::string& value);
inline std::string TrimView(std::string&& value);
inline size_t split(const std::string& s, std::vector<std::string>& result, std::string = ",", bool bTrim = true, bool bIgnoreEmpty = false);
inline void split_node_coord(const std::string& s, std::vector<std::string>& result,std::vector<int>& control);
inline size_t splitView(const std::string& s, std::vector<std::string>& resultView, std::string delim = ",", bool bTrim = true, bool bIgnoreEmpty = false);
//inline std::string stringSub(std::string str, int start, int len);
inline std::string stringSub(std::string& str, int start, int len);
inline std::string splitKey(const std::string& s, std::string delim = ",");
inline void removeInvalid(std::string& s); // ascii only for now
inline void removeChar(std::string& s, char ch);
inline std::string stringLeft(const std::string& cont, size_t cnt);
inline std::string stringRight(const std::string& cont, size_t cnt);
inline bool stringStartWith(const std::string& s, char c);
inline bool stringStartWith(const std::string& s, std::string& sLess);
inline bool stringStartWith(const std::string& s, std::string&& sLess);

bool string2Integer(std::string strString, int& nInterger);
bool string2Integer(std::string strString, SKey& nInterger);
bool string2Float(std::string strString, float& fFloat);
bool string2Float(std::string strString, double& ddouble);
bool string2Double(std::string strString, float& fReal);
bool string2Double(std::string strString, double& dReal);

template<typename containtype>
bool stringContain(const containtype& cont, std::string s)
{
	return std::search(cont.begin(), cont.end(), s.begin(), s.end()) != cont.end();
}

template<typename containtype>
int stringMatch(const containtype& cont, std::string s)
{
	auto it = std::search(cont.begin(), cont.end(), s.begin(), s.end());
	int idx = std::distance(cont.begin(), it); // O(n)
	return idx < cont.size() ? idx : -1;
}

template<typename containtype>
int stringFirstOf(const containtype& cont, std::string s)
{
	auto it = cont.find_first_of(s);
	return it == std::string::npos ? -1 : it;
}

template<typename containtype>
int stringLastOf(const containtype& cont, std::string s)
{
	auto it = cont.find_last_of(s);
	return it == std::string::npos ? -1 : it;
}

size_t split(const std::string& s, std::vector<std::string>& result, std::string delim/* = ","*/, bool bTrim/* = true*/, bool bIgnoreEmpty/* = false*/)
{
	result.clear();
	size_t first = 0;
	if (bTrim)
	{
		if (bIgnoreEmpty)
		{
			while (first < s.size()) {
				size_t second = s.find_first_of(delim, first);
				if (first != second)
				{
					std::string str(TrimView(s.substr(first, second - first)));
					if (str.size() > 0)
						result.emplace_back(str);
				}
				if (second == std::string::npos)
					break;
				first = second + 1;
			}
		}
		else
		{
			while (first < s.size()) {
				size_t second = s.find_first_of(delim, first);
				if (first != second)
				{
					std::string str(TrimView(s.substr(first, second - first)));
					result.emplace_back(str);
				}
				else
					result.emplace_back(std::string());
				if (second == std::string::npos)
					break;
				first = second + 1;
			}
		}
	}
	else {
		while (first < s.size()) {
			size_t second = s.find_first_of(delim, first);
			if (first != second)
				result.emplace_back(s.substr(first, second - first));
			else
				result.emplace_back("");
			if (second == std::string::npos)
				break;
			first = second + 1;
		}
	}
	return result.size();
}

void split_node_coord(const std::string& s, std::vector<std::string>& result, std::vector<int>& control)
{
	result.clear();
	result.resize(control.size());
	int start = 0, end = control[0];
	for (int i = 0;i< control.size();i++)
	{
		if (start + end <= s.size())
		{
			result[i] = Trim(s.substr(start, end));
			start += end;
			end = control[i + 1];
			if (start<s.size() && start + end>s.size())
			{
				end = s.size() - start;
			}
		}
		else {
			result[i] = '0.0';
		}
	}
};

//! for big string usage
size_t splitView(const std::string& s, std::vector<std::string>& resultView, std::string delim/* = ","*/, bool bTrim/* = true*/, bool bIgnoreEmpty/* = false*/)
{
	size_t first = 0;
	if (bTrim)
	{
		if (bIgnoreEmpty)
		{
			while (first < s.size()) {
				size_t second = s.find_first_of(delim, first);
				if (first != second)
				{
					std::string str(TrimView(s.substr(first, second - first)));
					if (str.size() > 0)
						resultView.push_back(str);
				}
				if (second == std::string::npos)
					break;
				first = second + 1;
			}
		}
		else
		{
			while (first < s.size()) {
				size_t second = s.find_first_of(delim, first);
				if (first != second)
				{
					std::string str(TrimView(s.substr(first, second - first)));
					resultView.emplace_back(str);
				}
				else
					resultView.emplace_back(std::string());
				if (second == std::string::npos)
					break;
				first = second + 1;
			}
		}

	} else {
		while (first < s.size()) {
			size_t second = s.find_first_of(delim, first);
			if (first != second)
				resultView.push_back(s.substr(first, second - first));
			if (second == std::string::npos)
				break;
			first = second + 1;
		}
	}
	return resultView.size();
}

std::string splitKey(const std::string& s, std::string delim/* = ","*/)
{
	size_t first = 0;
	while (first < s.size()) {
		size_t second = s.find_first_of(delim, first);
		if (first != second)
			return std::move(s.substr(first, second - first));
		if (second == std::string::npos)
			break;
		first = second + 1;
	}
	return std::string();
}

//! ascii only for now
void removeInvalid(std::string& s)
{
	s.erase(remove_if(s.begin(), s.end(), [](char c) { return !(c >= 32 && c <= 126); }), s.end());
}

void removeChar(std::string& s, char ch)
{
	s.erase(remove_if(s.begin(), s.end(), [ch](char c) { return c == ch; }), s.end());
}

std::string stringSub(std::string& str, int start, int len)
{
	if (std::min((int)str.size() - start, len) <= 0) {
		str.clear();
		return str;
	}
	return Trim(str.substr(start, len));
}

//std::string stringSub(std::string str, int start, int len)
//{
//	if (std::min((int)str.size() - start, len) <= 0) {
//		str = {};
//		return str;
//	}
//	return TrimView(str.substr(start, len));
//}

std::string& Trim(std::string& value)
{
	if (!value.empty())
	{
		size_t pos = value.find_first_not_of(" ");
		if (pos > 0)
			value.erase(0, pos);
		pos = value.find_last_not_of(" ");
		if (pos + 1 < value.size())
			value.erase(pos + 1);
	}
	return value;
}

std::string& Trim(std::string&& value)
{
	if (!value.empty())
	{
		size_t pos = value.find_first_not_of(" ");
		if (pos > 0)
			value.erase(0, pos);
		pos = value.find_last_not_of(" ");
		if (pos + 1 < value.size())
			value.erase(pos + 1);
	}
	return value;
}

std::string TrimView(std::string& value)
{
	std::string vv = value;
	if (!value.empty())
	{
		size_t pos = vv.find_first_not_of(" ");
		if (pos > 0)
			vv.erase(0, pos);
		//vv.substr(std::min(vv.find_first_not_of(" "), vv.size()));
		pos = vv.find_last_not_of(" ");
		vv.erase(pos + 1);
		//if (pos + 1 < vv.size())
		//	vv.substr(vv.size() - pos - 1);
		//pos = vv.find_last_not_of("\t");
		//if (pos + 1 < vv.size())
		//	vv.substr(vv.size() - pos - 1);
	}
	return vv;
}

std::string TrimView(std::string&& value)
{
	std::string vv = value;
	if (!value.empty())
	{
		size_t pos = vv.find_first_not_of(" ");
		if (pos > 0)
			vv.erase(0, pos);
		//vv.substr(std::min(vv.find_first_not_of(" "), vv.size()));

		pos = vv.find_last_not_of(" ");
		vv.erase(pos + 1);
		//if (pos + 1 < vv.size())
		//	vv.substr(vv.size() - pos - 1);
		//pos = vv.find_last_not_of("\t");
		//if (pos + 1 < vv.size())
		//	vv.substr(vv.size() - pos - 1);
	}
	return vv;
}

std::string stringLeft(const std::string& cont, size_t cnt)
{
	return (cnt < cont.size()) ? std::string(cont.data(), cnt) : std::string(cont);
}

std::string stringRight(const std::string& cont, size_t cnt)
{
	return (cnt < cont.size()) ? std::string(cont.end() - cnt, cont.end()) : std::string(cont);
}

bool stringStartWith(const std::string& s, char c)
{
	auto it = s.begin();
	if (s.size() > 0 && *it == c)
		return true;
	return false;
}

bool stringStartWith(const std::string& s, std::string& sLess)
{
	return sLess == std::string(s.c_str(), std::min(s.size(), sLess.size()));
}

bool stringStartWith(const std::string& s, std::string&& sLess)
{
	return sLess == std::string(s.c_str(), std::min(s.size(), sLess.size()));
}
